#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project ：Employment_class 
@File    ：10_tftp_download.py
@IDE     ：PyCharm 
@Author  ：XiaoDengYa
@Date    ：2021/6/23/023 9:54 
"""

import os
import struct
from socket import *


def main():
    # 0. 获取要下载的文件名字:
    downloadFileName = input("请输入要下载的文件名：")

    # 1、创建socket，0，octet（协议），0
    udpSocket = socket(AF_INET, SOCK_DGRAM)
    # 1 下载请求，文件名，
    requestFileData = struct.pack("!H%dsb5sb" % len(downloadFileName), 1, downloadFileName.encode("utf-8"), 0, "octet".encode("utf-8"), 0)

    # 2、发送下载文件的请求
    udpSocket.sendto(requestFileData, ("192.168.10.114", 69))

    flag = True  # 表示能够下载数据，即不擅长，如果是false那么就删除
    num = 0
    f = open(downloadFileName, "ab+") # 新建一个文件
    while True:
        # 3. 接收服务发送回来的应答数据
        responseData = udpSocket.recvfrom(1024)

        recvData, serverInfo = responseData

        opNum = struct.unpack("!H", recvData[:2]) # 接收后服务端发送回来的数据，3为有结果可传输

        packetNum = struct.unpack("!H", recvData[2:4]) # 获取服务器返回回来的序号（块编码）
        print("recvData:{}".format(recvData))
        print("opNum:{}".format(opNum))
        print(packetNum[0])

        # if 如果服务器发送过来的是文件的内容的话:
        if opNum[0] == 3:  # 因为opNum此时是一个元组(3,)，所以需要使用下标来提取某个数据

            # 计算出这次应该接收到的文件的序号值，应该是上一次接收到的值的基础上+1
            num = num + 1

            # 如果一个下载的文件特别大，即接收到的数据包编号超过了2个字节的大小
            # 那么会从0继续开始，所以这里需要判断，如果超过了65535 那么就改为0
            if num == 65536:
                num = 0

            # 判断这次接收到的数据的包编号是否是 上一次的包编号的下一个
            # 如果是才会写入到文件中，否则不能写入（因为会重复）
            if num == packetNum[0]:
                # 把收到的数据写入到文件中
                f.write(recvData[4:])
                print("recvdata:{}".format(recvData[4:]))
                num = packetNum[0]

            # 整理ACK的数据包
            ackData = struct.pack("!HH", 4, packetNum[0]) # 把接收到的服务器编码重新整理返回给服务器告诉他我已经收到
            udpSocket.sendto(ackData, serverInfo)
        elif opNum[0] == 5:
            print("sorry，没有这个文件....丢包了")
            flag = False

        if len(recvData) < 516:
            break

    if flag == True:
        f.close()
    else:
        os.unlink(downloadFileName)  # 如果没有要下载的文件，那么就需要把刚刚创建的文件进行删除


if __name__ == '__main__':
    main()

